package com.foxitcloud.webpdf;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.UnsupportedEncodingException;
import java.util.Date;

import org.apache.commons.lang3.mutable.MutableLong;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.foxitcloud.util.SpringUtil;
import com.foxitcloud.webpdf.cache.store.FileSystemCacheStore;
import com.foxitcloud.webpdf.calldll.FXPDFError;
import com.foxitcloud.webpdf.calldll.FXPDFFormHandle;
import com.foxitcloud.webpdf.calldll.FXPDFToImageHandle;
import com.foxitcloud.webpdf.calldll.FormMgrHelper;
import com.foxitcloud.webpdf.calldll.PDFToImageMgrHelper;
import com.foxitcloud.webpdf.calldll.WebReaderDllLoader;
import com.foxitcloud.webpdf.common.CacheDescriptionHandle;
import com.foxitcloud.webpdf.common.Config;
import com.foxitcloud.webpdf.common.ConfigConstans;
import com.foxitcloud.webpdf.common.OperationData;
import com.foxitcloud.webpdf.common.OperationType;
import com.foxitcloud.webpdf.common.PDFDocType;
import com.foxitcloud.webpdf.common.Signature;
import com.foxitcloud.webpdf.exception.CacheException;
import com.foxitcloud.webpdf.utils.AESUtils;
import com.foxitcloud.webpdf.utils.FXFileUtils;
import com.foxitcloud.webpdf.utils.FXStringUtils;

public class ConversionPDF {
	private static final Logger log = LoggerFactory.getLogger(ConversionPDF.class);
	
	private String s3Key;
	private String filePath;
	private boolean isFormMode = false;
	private boolean isForm = false;
	private boolean isTrail = false;
	private String pdfDocPassword;
	private FXPDFToImageHandle pdf2img = null;
	private FXPDFFormHandle formHandle = null;
	private static String pageImageZoomList[] = null;
	
	private int pageCount = -1;
	
	//@Autowired
	private FileSystemCacheStore cacheStoreProvider = (FileSystemCacheStore) SpringUtil.getBean("cacheStoreProvider");;
	
	static {
		WebReaderDllLoader.loadDlls(Config.getValue(ConfigConstans.FOXIT_WEB_READER_DLL_DIR, ""));
	}
	
	public ConversionPDF(String s3Key, String filePath, String pdfDocPassword, boolean isFormMode) {
		this.s3Key = s3Key;
		this.filePath = filePath;
		this.pdfDocPassword = pdfDocPassword;
		this.isFormMode = isFormMode;
		if (pageImageZoomList == null) {
			pageImageZoomList = Config.getValue(
					ConfigConstans.IMAGE_ZOOM_LIST_KEY,
					ConfigConstans.DEFAULT_IMAGE_ZOOM_LIST).split(",");
		}
	}
	
	public int parser() throws IOException {
		log.info("parser pdf file : " + filePath);
		byte[] pdfStream = FXFileUtils.readToByteArray(filePath);
		String fontPath = Config.getValue(
				ConfigConstans.FONT_ROOT_PATH_KEY, "");
		
		long error = FXPDFError.ERROR_SUCCESS;

		if (pdfStream != null) {
			pdf2img = PDFToImageMgrHelper.getPdf2ImgMgr(fontPath)
					.createPDFToImgHandler();
			error = pdf2img.parsePDFDocument(pdfStream, pdfDocPassword);
		} else {
			error = FXPDFError.ERROR_PDFPARSE_FILE;
		}
		
		if (FXPDFError.ERROR_SUCCESS != error) {
			log.warn(String.format(
					"Parse document %s failed,error code:%d",
					filePath, error));
			return (int)error;
		}
		
		if (pdfDocPassword == null)
			pdfDocPassword = "";
		else {
			pdfDocPassword = AESUtils.encryptAES(pdfDocPassword,
					ConfigConstans.FOXIT_DOC_PASSWORD_DEFAULTPASS);
		}
		
		int maxmiumPageCount = Config.getValue(
				ConfigConstans.MAX_PAGE_NUMBER_KEY,
				ConfigConstans.DEFAULT_MAX_PAGE_NUMBER);
		pageCount = pdf2img.getPageCount();
		if (pageCount > maxmiumPageCount) {
			log.warn(String.format("Convert document %s failed because page count exceeding %d, page count:%d",
							filePath, maxmiumPageCount, pageCount));
			return FXPDFError.ERROR_PDFPARSE_PAGE_COUNT_EXCEED;
		}
		
		int docType = pdf2img.getDocType();
		//是否表单转换模式，如果是的话，manifest数据要添加form标记
		this.isForm = (docType == PDFDocType.DocType_FORM);
		if (this.isForm) {
			if (this.isFormMode) {
				String msgInfo = String
						.format("Document %s is a AcroForm, use form mode to convert.",
								filePath);
				log.debug(msgInfo);
				long docHandle = pdf2img.getPdfDocHandle();
				formHandle = FormMgrHelper.getPdfFormMgr(fontPath).createPDFFormHandlerEx(docHandle);
			} else {
				String msgInfo = String
						.format("Document %s is a AcroForm, but do not use form mode to convert.",
								filePath);
				log.debug(msgInfo);
			}
		} else {
			if (this.isFormMode) {
				String msgInfo = String
						.format("Document %s is not a AcroForm, do not use form mode to convert.",
								filePath);
				log.debug(msgInfo);
			}
			isForm = false;
		}
		
		// images
		int verticalDPI = Config.getValue(
				ConfigConstans.DEFAULT_IMAGE_VERTICAL_DPI_KEY,
				ConfigConstans.DEFAULT_IMAGE_VERTICAL_DPI);
		int horizontalDPI = Config.getValue(
				ConfigConstans.DDEFAULT_IMAGE_HORIZONTAL_DPI_KEY,
				ConfigConstans.DDEFAULT_IMAGE_HORIZONTAL_DPI);
		int maxImageHeight = Config.getValue(
				ConfigConstans.MAX_IMAGE_HEIGHT_KEY,
				ConfigConstans.DEFAULT_MAX_IMAGE_HEIGHT);
		int maxImageWidth = Config.getValue(
				ConfigConstans.MAX_IMAGE_WIDTH_KEY,
				ConfigConstans.DEFAULT_MAX_IMAGE_WIDTH);
		pdf2img.setImageDPI(horizontalDPI, verticalDPI);
		pdf2img.setMaxImageSize(maxImageWidth, maxImageHeight);
		return FXPDFError.ERROR_SUCCESS;
	}
	
	//manifest、document properties、annotation、bookmark
	public void createDocInfo() throws UnsupportedEncodingException, CacheException {
		String manifest = CacheDescriptionHandle
				.getManifestInfoCacheDescription(isFormMode,isTrail);
		String manifestPath = cacheStoreProvider.getCacheItemPath(this.s3Key, manifest);
		if (new File(manifestPath).exists()) {
			log.info("Document information already exists, do not need to extract.");
			return;
		}
		
		// annotation data
		String annotsJsonInfo = pdf2img.getAnnotData();
		if (FXStringUtils.IsNullOrEmpty(annotsJsonInfo))
			annotsJsonInfo = "{}";
		cacheStoreProvider.addItemToCache(this.s3Key,
				CacheDescriptionHandle.getAnnnosCacheDescription(isTrail),
				annotsJsonInfo.getBytes("UTF-8"), pdfDocPassword, isTrail);

		// bookmark data
		String bookmarkJsonInfo = pdf2img.getBookmarkInfo();
		if (FXStringUtils.IsNullOrEmpty(bookmarkJsonInfo))
			bookmarkJsonInfo = "{}";
		cacheStoreProvider.addItemToCache(this.s3Key,
				CacheDescriptionHandle.getBookmarksCacheDescription(isTrail),
				bookmarkJsonInfo.getBytes("UTF-8"), pdfDocPassword, isTrail);
		
		// manifest data
		String strManifestInfo = pdf2img.getManifestInfo();
		StringBuffer strPagesInfoBuffer = new StringBuffer(
				strManifestInfo);
		// get document properties
		String docproperty = ",\"dppinfo\":"
				+ pdf2img.getDocProperties() + ",\"createdUtcTime\":"
				+ (new Date()).getTime();
		strManifestInfo = strPagesInfoBuffer.insert(
				strManifestInfo.length() - 2, docproperty).toString();
		if (FXStringUtils.IsNullOrEmpty(strManifestInfo))
			strManifestInfo = "{}";
		byte manifestBinaryData[] = strManifestInfo.getBytes("UTF-8");
		cacheStoreProvider.addItemToCache(this.s3Key, manifest,
				manifestBinaryData, pdfDocPassword,isTrail);
	}
	
	public int readerFile() {
		int ret = FXPDFError.ERROR_SUCCESS;
		for (int i=0; i<this.pageCount; i++) {
			int readerPageRet = this.readerPage(i);
			if (readerPageRet != FXPDFError.ERROR_SUCCESS) {
				ret = readerPageRet;
			}
		}
		return ret;
	}
	
	public int readerPage(int pageIndex) {
		log.info(" converting page: " + pageIndex + "\n");
		
		String textCacheDescription = CacheDescriptionHandle
				.getPageTextCacheDescription(pageIndex,isTrail); 
		String formXmlCacheDescription = CacheDescriptionHandle
				.getFormXmlInfoCacheDescription(pageIndex, isTrail);
		
		if (new File(isForm ? formXmlCacheDescription: textCacheDescription).exists()) {
			log.info("Page(" + String.valueOf(pageIndex) + ") information already exists, do not need to extract.");
			return FXPDFError.ERROR_SUCCESS;
		}
		
		long pageHandle = 0;
		long error = 0;
		try {
			MutableLong result = new MutableLong();
			pageHandle = pdf2img.getPageHandle(pageIndex);
			for (String zoom : pageImageZoomList) {
				boolean isShowWidget = true;
				float fZoom = Float.parseFloat(zoom);
				if (isForm && fZoom != -1.0f) {
					isShowWidget = false;
				}
				//add by veikin 将原图放大为2倍
				float t_fZoom = -1.0f;
				if(1.0f == fZoom){
					t_fZoom = 2.0f;
				}
				//end here
				byte[] imgStream = pdf2img.createPageImage(pageHandle,
						pageIndex, t_fZoom, isShowWidget, result);
				error = result.getValue();
				if (error != FXPDFError.ERROR_SUCCESS) {
					log.info("create image faild, page index: "
							+ pageIndex + "\n");
					break;
				} else {
					String cacheDescription = CacheDescriptionHandle
							.getPageImageCacheDescription(pageIndex,
									fZoom, (fZoom == -1.0f) ? false : isForm, isTrail);
					cacheStoreProvider.addItemToCache(this.s3Key,
							cacheDescription, imgStream, pdfDocPassword, isTrail);
					log.info("save success: " + cacheDescription);
				}

				if (-1.0f == fZoom) // 缩略图
					continue;

				String pageText = pdf2img.getPageText(pageHandle,
						pageIndex, result);
				error = result.getValue();
				if (error != FXPDFError.ERROR_SUCCESS) {
					break;
				}
				if (FXStringUtils.IsNullOrEmpty(pageText))
					pageText = "{}";
				
				cacheStoreProvider.addItemToCache(this.s3Key, textCacheDescription,
						pageText.getBytes("UTF-8"), pdfDocPassword,isTrail);
				log.info("save success: " + textCacheDescription);
			}
			if (error == FXPDFError.ERROR_SUCCESS && isForm) {
				StringBuffer xmlInfoBuffer = new StringBuffer("");
				byte[] formImgData = formHandle.outputBGImageToBuf(
						pageIndex, true, xmlInfoBuffer,result);
				error = result.getValue();
				if (error == FXPDFError.ERROR_SUCCESS) {
					String formCacheDescription = CacheDescriptionHandle
							.getFormImageCacheDescription(pageIndex,isTrail);
					cacheStoreProvider.addItemToCache(this.s3Key,
							formCacheDescription, formImgData, pdfDocPassword, isTrail);
					log.info("save success: " + formCacheDescription);
					String formXmlInfo = xmlInfoBuffer.toString();//formHandle.getFromXmlInfo(result);
					if (FXStringUtils.IsNullOrEmpty(formXmlInfo)) {
						formXmlInfo = "{}";
					}
					cacheStoreProvider.addItemToCache(this.s3Key, formXmlCacheDescription,
							formXmlInfo.getBytes("UTF-8"), pdfDocPassword, isTrail);
					log.info("save success: " + formXmlCacheDescription);
				}
			}
		} catch (CacheException ex) {
			log.warn(String.format("Unexpected exception occured while converting document %s",
							this.filePath), ex);
			return FXPDFError.ERROR_CACHE_EXCEPTION;
		} catch (Exception ex) {
			log.warn(String.format("Unexpected exception occured while converting document %s",
					this.filePath), ex);
			return FXPDFError.ERROR_EXCEPTION;
		} finally {
			if (pageHandle != 0) {
				pdf2img.releasePageHandle(pageHandle);
				pageHandle = 0;
			}
		}
		return FXPDFError.ERROR_SUCCESS;
	}
	
	public String operationFile(int type, String operationInfoPath){
		String resPath = "";
		OperationData operationData = null;
		InputStream infoInputStream = cacheStoreProvider.getCachedStream(this.s3Key, operationInfoPath, false);
		if (infoInputStream != null) {
			try {
				ObjectInputStream objectInputStream = new ObjectInputStream(infoInputStream);
				operationData = (OperationData) objectInputStream.readObject();
				objectInputStream.close();
			} catch (Exception e) {
				e.printStackTrace();
				log.warn("get operation file data fail");
			}
			try {
				cacheStoreProvider.removeCacheItem(this.s3Key, operationInfoPath);
			} catch (CacheException e) {}
		}
		
		if(null == operationData || null == pdf2img)
			return resPath;
		
		if (OperationType.OperationType_Save ==type) {				
			//Import FormData
			if(this.isForm && null != operationData.getFromData()){				
				byte[] formXmlData = operationData.getFromData();
				if(null == formHandle || 0 != formHandle.importFromXMLFile(formXmlData)) {
					log.info("update form data faild.");
					return resPath;
				}
				log.info("update form data success.");
			}
			
			//Add Signature
			if(null != operationData.getSignatureData()){
				String signData = "";
				try {
					signData = new String(operationData.getSignatureData(),"UTF-8");
				} catch (UnsupportedEncodingException e) {
					// TODO Auto-generated catch block
					e.printStackTrace();
				}
				Signature signature = new Signature(signData);
				if(!pdf2img.addSignateure(signature)) {
					log.info("Add signatuer failed.");
					return resPath;
				}
				log.info("Add signatuer success.");
			}		
			
			//add annot
			if(null != operationData.getAnnotData()){
				String annotData =operationData.getAnnotData();
				if(0 != pdf2img.addAnnot(annotData))
					return resPath;
			}
			
			//设置生成pdf路径的函数
			resPath = this.cacheStoreProvider.getCacheItemPath(this.s3Key, 
					"operation/pdfDoc" + String.valueOf(System.currentTimeMillis()) + ".pdf");
			if (!pdf2img.saveAsPdf(resPath)) {
				return "";
			}
		}			
		else if(OperationType.OperationType_Import == type){
			
		}		
		
		return resPath;
	}
	
	
	public String getPdfText() throws CacheException{
		if(null == pdf2img)
			return "";
		
		String textCacheDescription = CacheDescriptionHandle.getPdfTextCacheDescription();
		String textPath = cacheStoreProvider.getCacheItemPath(this.s3Key, textCacheDescription);
		if (new File(textPath).exists()) {
			return textPath;
		}
		
		String pdfTextData = pdf2img.getPdfText();		
		cacheStoreProvider.addItemToCache(this.s3Key,
				textCacheDescription, pdfTextData, pdfDocPassword, isTrail);
		return textPath;
	}
	
	
	public void release() {
		if (null != pdf2img) {
			pdf2img.release();
			pdf2img = null;
		}

		if (null != formHandle) {
			formHandle.release();
			formHandle = null;
		}
	}
}
